package networkSimulation.simulator

import xml.XML
import networkSimulation.framework.{Observable, Edge, Node, Graph}
import java.io.{File, IOException}

/**
 * Abstract class, representing a simulator for our framework
 *
 * @author  Tim Caljé
 *          Jeroen De Raedt
 *          Jorick Van Aken
 */
abstract class Simulator[N<%Node,E<%Edge[N]](var graph: Graph[N, E]) extends Observable{

  /**
   * Run a single simulation
   */
  def run() {
    this.graph.nodes.map(n => n.simulate(this.graph))
  }

  /**
   * Store an XML representation of the graph in a file
   */
  def storeGraph(fileLocation: String) {
    scala.xml.XML.save(fileLocation, this.graph.toXml())
  }

  /**
   * Load a stored XML representation of the graph.
   * The existing graph will be erased.
   */
  def load(fileLocation: String) {
    try {
      val data = XML.loadFile(fileLocation)
      if ((data \\ "graph" \ "@tpe").text != this.graph.getGraphType()) {
        throw new IOException("The provided XML is for a different graph type")
      }
      this.graph.resetGraph()
      val nodes = data \\ "graph" \\ "nodes"
      this.loadNodes(nodes)
      val edges = data \\ "graph" \\ "edges"
      this.loadEdges(edges)
    } catch {
      case e: Exception =>
        this.graph.resetGraph() // if something goes wrong, we make sure to reset the entire graph
        throw e
    }
  }

  /**
   * Load nodes from XML
   */
  private def loadNodes(nodes : xml.NodeSeq) {
    for (node <- (nodes \\ "node")) {
      this.graph.addNode(this.loadNode(node).asInstanceOf[N])
    }
  }

  /**
   * Load a single node from XML
   */
  protected def loadNode(node: xml.NodeSeq) = {
    var name : String = ""
    for(field <- (node \\ "field")) {
      (field \\ "@name").text match {
        case "name" => name = field.text
        case _ => throw new Exception("Tried loading invalid node with unknown field")
      }
    }
    new Node(name)
  }

  /**
   * Load the edges from XML
   */
  private def loadEdges(edges : xml.NodeSeq) {
    for (edge <- (edges \\ "edge")) {
      this.graph.addEdge(this.loadEdge(edge).asInstanceOf[E])
    }
  }

  /**
   * Load a single edge from XML
   */
  protected def loadEdge(edge: xml.NodeSeq) = {
    var from = new Node("dummy")  // we require that each node has a name,
    var to = new Node("dummy")    // later on we'll check whether these dummy values are gone
    for(field <- (edge \\ "field")) {
      (field \\ "@name").text match {
        case "from" => from = this.graph.getNode(field.text)
        case "to" => to = this.graph.getNode(field.text)
        case _ => throw new Exception("Tried loading invalid edge with unknow field")
      }
    }
    // check whether the dummy values are gone
    if(from.name == "dummy" || to.name == "dummy") {
      throw new Exception("Missing properties for edge")
    }
    new Edge(from, to)
  }

  /**
   * Clean the output folder of the graph
   */
  protected def clean(path: String) {
    var k = 0
    var file3 = new File(path+ k+".gif")
    while(file3.exists) {
      file3.delete()
      k+=1
      file3 = new File(path+ k+".gif")
    }
  }

}